package net.fltx.p2p.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * p2p客户端
 */
public abstract class P2PClient {
    private static final Logger log= LoggerFactory.getLogger(P2PClient.class);
    /** p2p监听 */
    protected ServerSocketChannel monitorSocket;
    /** p2p中心服务端 */
    protected SocketChannel  centerServer;
    /**
     * 本地绑定地址
     */
    protected SocketAddress bindLocalAddress;

    /**
     * 服务器地址
     */
    protected SocketAddress serverAddress;

    /** 线程池 */
    protected ExecutorService executorService;
    protected Selector selector;



    public P2PClient() {
        executorService=Executors.newFixedThreadPool(16);
    }

    /**
     * 连接服务器
     * @throws IOException
     */
    public void connectServer() throws IOException {
        if(centerServer==null || centerServer.socket().isClosed()){
            centerServer=SocketChannel.open();
            centerServer.configureBlocking(false);
            if(bindLocalAddress!=null){
                centerServer.bind(bindLocalAddress);
            }
            centerServer.connect(serverAddress);
            centerServer.register(selector, SelectionKey.OP_READ);
            bindLocalAddress=centerServer.getLocalAddress();
            onServerConnected(centerServer);
        }
    }

    /**
     * 初始化
     */
    public void init(){

    }

    /**
     * 启动
     */
    public void start(){
        executorService.execute(()->{
            while (true){
                try {
                    if(selector.select(1000)==0){
                        Thread.yield();
                        continue;
                    }
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()){
                        SelectionKey next = iterator.next();
                        iterator.remove();
                        if(next.isReadable()){ //读取数据事件
                            next.interestOps(next.interestOps()&~SelectionKey.OP_READ);
                            executorService.execute(()->{
                                if(next.channel()==centerServer){
                                    onServerReadable(next);
                                }else {
                                    onClientReadable(next);
                                }
                                next.interestOps(next.interestOps()&~SelectionKey.OP_READ);
                            });
                        }

                        if(next.isAcceptable()){
                            next.interestOps(next.interestOps()&~SelectionKey.OP_ACCEPT);
                            executorService.execute(()->{
                                SocketChannel accept = null;
                                try {
                                    accept = monitorSocket.accept();
                                    accept.register(selector,SelectionKey.OP_READ);
                                    onClientConnected(accept);
                                    next.interestOps(next.interestOps()|SelectionKey.OP_ACCEPT);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                    try {
                                        next.channel();
                                        monitorSocket.close();
                                    } catch (IOException exception) {
                                        exception.printStackTrace();
                                    }
                                }

                            });
                        }
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public abstract void onServerConnected(SocketChannel server);
    public abstract void onClientConnected(SocketChannel client);

    public abstract void onClientReadable(SelectionKey key);
    public abstract void onServerReadable(SelectionKey key);





}
